import vnode  from './vnode';
import createElement from './createElement';

function isSameVnode(oldVnode, newVnode) {
    return oldVnode.sel === newVnode.sel && oldVnode.key === newVnode.key;
}

function updateChild(parentElm, oldCh, newCh) {
    let oldStartIdx = 0;
    let oldEndIdx = oldCh.length - 1;
    let oldStartVnode = oldCh[0];
    let oldEndVnode = oldCh[oldEndIdx];

    let newStartIdx = 0;
    let newEndIdx = newCh.length - 1;
    let newStartVnode = newCh[0];
    let newEndVnode = newCh[newEndIdx];

    let keyMap = null;
    while (oldStartIdx <= oldEndIdx && newStartVnode <= newEndIdx) {
        if (!oldStartVnode) {
            oldStartVnode = oldCh[++oldStartIdx]
        } else if (!oldEndVnode) {
            oldEndVnode = oldCh[--oldEndIdx]
        } else if (!newStartVnode) {
            newStartVnode = newCh[++newStartIdx]
        } else if (!newEndVnode) {
            newEndVnode = newCh[--newEndIdx]
        } else if (isSameVnode(oldStartVnode, newStartVnode)) {
            // 新前 与 旧前
            patchVnode(oldStartVnode, newStartVnode);
            oldStartVnode = oldCh[++oldStartIdx];
            newStartVnode = newCh[++newStartIdx];
        } else if (isSameVnode(oldEndVnode, newEndVnode)) {
            // 新后与旧后
            patchVnode(oldEndVnode, newEndVnode);
            oldEndVnode = oldCh[--oldEndIdx];
            newEndVnode = newCh[--newOldIdx];
        } else if (isSameVnode(oldStartVnode, newEndVode)) {
            // 新后 与 旧前
            patchVnode(oldStartVnode, newEndVnode);
            // 移动节点
            parentElm.insertBefore(oldStartVnode,oldEndVnode.elm.nextSibling);
            oldStartVnode = oldCh[++oldStartIdx];
            newEndVnode = newCh[--newEndIdx];
        } else if (isSameVnode(oldEndVnode.elm, newStartVnode)) {
            // 新前与旧后
            patchVnode(oldEndVnode, newStartVnode);
            // 移动节点
            parentElm.insertBefore(oldEndVnode.elm, oldStartVnode.elm);
            oldEndVnode = oldCh[--oldEndIdx];
            newStartVnode = newCh[++newStartIdx]; 
        } else {
            if (!keyMap) {
                keyMap = Object.create(null);
                for(let i = oldStartIdx; i <= oldEndIdx; i++) {
                    keyMap[oldCh[i].key] = i
                }
            }
            const oldKey2Vnode = oldCh[keyMap[newStartVnode.key]];
            if (!oldKey2Vnode) {
                // 说明是新增节点，需要添加
                parentElm.insertBefore(createElement(newStartVnode), oldStartVnode.elm);
            } else {
                if (oldKey2Vnode.sel === newStartVnode.sel) {
                    patch(oldKey2Vnode,newStartVnode);
                    // 移动节点
                    parentElm.insertBefore(oldKey2Vnode.elm, oldStartVnode.elm);
                    oldCh[keyMap[newStartVnode.key]] = undefined;
                } else {
                    // 直接插入结点
                    parentElm.insertBefore(createElement(newStartVnode), oldStartVnode.elm);
                }
            }
            newStartVnode = newCh[++newStartIdx]
        }
    }
    if (oldStartIdx <= oldEndIdx) {
        for(let i = oldStartIdx; i <= oldEndIdx; i++) {
            parentElm.removeChild(oldCh[i].elm);
        }
    } else if (newStartIdx <= newEndIdx) {
        let before = oldCh[oldEndIdx + 1] ?? null
        for(let i = newStartIdx; i <= newEndIdx; i++) {
            parentElm.insertBefore(createElement(newCh[i]), before);
        }
    }
}

export default function patch(oldVnode, newVnode) {
    // 是否真实DOM,如果是真实DOM，封装为虚拟节点
    if (oldVnode.nodeType) {
        oldVnode = vnode(oldVnode.tagName.toLowerCase(), {}, [], undefined, oldVnode);
        
    }
    // 判断是否为同一个节点,key和sel相同，则为同一个节点
    if (isSameVnode(oldVnode, newVnode)) {
        if (newVnode.text) {
            if (newVnode.text !== oldVnode.text) {
                oldVnode.textContent = newVnode.text;
            }
        } else if (newVnode.children && newVnode.children.length) {
            if (oldVnode.children && oldVnode.children.length) {
                updateChild(oldVnode.elm.parentNode, oldVnode.children, newVnode.children);
            } else {
                const parentElm = oldVnode.elm.parentNode();
                parentElm.innerHtml = "";
                newVnode.children.forEach(child => {
                    parentElm.appendChild(createElement(child))
                })
            }
        }
    }  else {
        const elm = createElement(newVnode);
        oldVnode.elm.parentNode.insertBefore(elm, oldVnode.elm);
        oldVnode.elm.parentNode.removeChild(oldVnode.elm);
    }
}